C# in Depth 4th Edition
contents
foreword xvii
preface xix
acknowledgments xx
about this book xxii
about the author xxvi
about the cover illustration xxvii
P ART 1 C# IN CONTEXT ............................................... 1
1
Survival of the sharpest 3
1.1 An evolving language 3
A helpful type system at large and small scales 4
■
Ever more
concise code 6
■
Simple data access with LINQ 9
Asynchrony 10
■
Balancing efficiency and complexity 11
Evolution at speed: Using minor versions 12
1.2 An evolving platform 13
1.3 An evolving community 14
1.4 An evolving book 15
Mixed-level coverage 16
■
Examples using Noda Time 16
Terminology choices 17
CONTENTS viii
P ART 2 C# 2–5 .......................................................... 19
2
C# 2 21
2.1 Generics 22
Introduction by example: Collections before generics 22
Generics save the day 25
■
What can be generic? 29
Type inference for type arguments to methods 30
■
Type
constraints 32
■
The default and typeof operators 34
Generic type initialization and state 37
2.2 Nullable value types 38
Aim: Expressing an absence of information 39
■ CLR and
framework support: The Nullable<T> struct 40
■ Language
support 43
2.3 Simplified delegate creation 49
Method group conversions 50
■ Anonymous methods
50
Delegate compatibility 52
2.4 Iterators 53
Introduction to iterators 54
■
Lazy execution 55
■
Evaluation
of yield statements 56
■
The importance of being lazy 57
Evaluation of finally blocks 58
■
The importance of finally
handling 61
■ Implementation sketch
62
2.5 Minor features 66
Partial types 67
■ Static classes
69
■ Separate getter/setter
access for properties 69
■
Namespace aliases 70
Pragma directives 72
■ Fixed-size buffers
73
InternalsVisibleTo 73
3
C# 3: LINQ and everything that comes with it 75
3.1 Automatically implemented properties 76
3.2 Implicit typing 77
Typing terminology 77
■
Implicitly typed local variables
(var) 78
■
Implicitly typed arrays 79
3.3 Object and collection initializers 81
Introduction to object and collection initializers 81
Object initializers 83
■
Collection initializers 84
The benefits of single expressions for initialization 86
3.4 Anonymous types 86
Syntax and basic behavior 86
■
The compiler-generated
type 89
■
Limitations 90
CONTENTS ix
3.5 Lambda expressions 91
Lambda expression syntax 92
■
Capturing variables 94
Expression trees 101
3.6 Extension methods 103
Declaring an extension method 103
■
Invoking an extension
method 104
■
Chaining method calls 106
3.7 Query expressions 107
Query expressions translate from C# to C# 108
■
Range
variables and transparent identifiers 108
■
Deciding when
to use which syntax for LINQ 110
3.8 The end result: LINQ 111
4
C# 4: Improving interoperability 113
4.1 Dynamic typing 114
Introduction to dynamic typing 114
■
Dynamic behavior
beyond reflection 119
■ A brief look behind the scenes
124
Limitations and surprises in dynamic typing 127
■ Usage
suggestions 131
4.2 Optional parameters and named arguments 133
Parameters with default values and arguments with names 134
Determining the meaning of a method call 135
■ Impact on
versioning 137
4.3 COM interoperability improvements 138
Linking primary interop assemblies 139
■ Optional parameters
in COM 140
■
Named indexers 142
4.4 Generic variance 143
Simple examples of variance in action 143
■
Syntax for
variance in interface and delegate declarations 144
Restrictions on using variance 145
■
Generic variance in
practice 147
5
Writing asynchronous code 150
5.1 Introducing asynchronous functions 152
First encounters of the asynchronous kind 152
■
Breaking
down the first example 154
5.2 Thinking about asynchrony 155
Fundamentals of asynchronous execution 155
■
Synchronization
contexts 157
■
Modeling asynchronous methods 158
CONTENTS x
5.3 Async method declarations 160
Return types from async methods 161
■
Parameters in async
methods 162
5.4 Await expressions 162
The awaitable pattern 163
■
Restrictions on await
expressions 165
5.5 Wrapping of return values 166
5.6 Asynchronous method flow 168
What is awaited and when? 168
■ Evaluation of await
expressions 169
■ The use of awaitable pattern members
173
Exception unwrapping 174
■
Method completion 176
5.7 Asynchronous anonymous functions 180
5.8 Custom task types in C# 7 182
The 99.9% case: ValueTask<TResult> 182
■
The 0.1% case:
Building your own custom task type 184
5.9 Async main methods in C# 7.1 186
5.10 Usage tips 187
Avoid context capture by using ConfigureAwait (where
appropriate) 187
■
Enable parallelism by starting multiple
independent tasks 189
■
Avoid mixing synchronous and
asynchronous code 190
■
Allow cancellation wherever
possible 190
■
Testing asynchrony 191
6
Async implementation 193
6.1 Structure of the generated code 195
The stub method: Preparation and taking the first step 198
Structure of the state machine 199
■
The MoveNext() method
(high level) 202
■
The SetStateMachine method and the state
machine boxing dance 204
6.2 A simple MoveNext() implementation 205
A full concrete example 205
■
MoveNext() method general
structure 207
■
Zooming into an await expression 209
6.3 How control flow affects MoveNext() 210
Control flow between await expressions is simple 211
Awaiting within a loop 212
■
Awaiting within a try/finally
block 213
6.4 Execution contexts and flow 216
6.5 Custom task types revisited 218
CONTENTS xi
7
C# 5 bonus features 220
7.1 Capturing variables in foreach loops 220
7.2 Caller information attributes 222
Basic behavior 222
■
Logging 224
■
Simplifying
INotifyPropertyChanged implementations 224
■
Corner cases of
caller information attributes 226
■
Using caller information
attributes with old versions of .NET 232
P ART 3 C# 6 ............................................................ 233
8
Super-sleek properties and expression-bodied members 235
8.1 A brief history of properties 236
8.2 Upgrades to automatically implemented properties 238
Read-only automatically implemented properties 238
Initializing automatically implemented properties 239
Automatically implemented properties in structs 240
8.3 Expression-bodied members 242
Even simpler read-only computed properties 242
■
Expression-
bodied methods, indexers, and operators 245
■
Restrictions on
expression-bodied members in C# 6 247
■
Guidelines for using
expression-bodied members 249
9
Stringy features 252
9.1 A recap on string formatting in .NET 253
Simple string formatting 253
■ Custom formatting with format
strings 253
■
Localization 255
9.2 Introducing interpolated string literals 258
Simple interpolation 258
■
Format strings in interpolated string
literals 259
■
Interpolated verbatim string literals 259
Compiler handling of interpolated string literals (part 1) 261
9.3 Localization using FormattableString 261
Compiler handling of interpolated string literals (part 2) 262
Formatting a FormattableString in a specific culture 263
Other uses for FormattableString 265
■
Using FormattableString
with older versions of .NET 268
9.4 Uses, guidelines, and limitations 270
Developers and machines, but maybe not end users 270
Hard limitations of interpolated string literals 272
■
When you
can but really shouldn’t 273
CONTENTS xii
9.5 Accessing identifiers with nameof 275
First examples of nameof 275
■
Common uses of nameof 277
Tricks and traps when using nameof 280
10
A smörgåsbord of features for concise code 284
10.1 Using static directives 284
Importing static members 285
■
Extension methods and using
static 288
10.2 Object and collection initializer enhancements 290
Indexers in object initializers 291
■
Using extension methods in
collection initializers 294
■ Test code vs. production code
298
10.3 The null conditional operator 299
Simple and safe property dereferencing 299
■
The null conditional
operator in more detail 300
■
Handling Boolean
comparisons 301
■ Indexers and the null conditional
operator 302
■
Working effectively with the null conditional
operator 303
■
Limitations of the null conditional operator 305
10.4 Exception filters 305
Syntax and semantics of exception filters 306
■ Retrying
operations 311
■
Logging as a side effect 312
■ Individual,
case-specific exception filters 313
■
Why not just throw? 314
P ART 4 C# 7 AND BEYOND ....................................... 317
11
Composition using tuples 319
11.1 Introduction to tuples 320
11.2 Tuple literals and tuple types 321
Syntax 321
■
Inferred element names for tuple literals
(C# 7.1) 323
■
Tuples as bags of variables 324
11.3 Tuple types and conversions 329
Types of tuple literals 329
■
Conversions from tuple literals to tuple
types 330
■
Conversions between tuple types 334
■
Uses of
conversions 336
■
Element name checking in inheritance 336
Equality and inequality operators (C# 7.3) 337
11.4 Tuples in the CLR 338
Introducing System.ValueTuple<...> 338
■
Element name
handling 339
■
Tuple conversion implementations 341
String representations of tuples 341
■
Regular equality and
ordering comparisons 342
■
Structural equality and ordering
CONTENTS xiii
comparisons 343
■
Womples and large tuples 345
■
The
nongeneric ValueTuple struct 346
■
Extension methods 346
11.5 Alternatives to tuples 346
System.Tuple<...> 347
■
Anonymous types 347
Named types 348
11.6 Uses and recommendations 348
Nonpublic APIs and easily changed code 348
■
Local
variables 349
■
Fields 350
■
Tuples and dynamic don’t play
together nicely 351
12
Deconstruction and pattern matching 353
12.1 Deconstruction of tuples 354
Deconstruction to new variables 355
■
Deconstruction
assignments to existing variables and properties 357
Details of tuple literal deconstruction 361
12.2 Deconstruction of nontuple types 361
Instance deconstruction methods 362
■
Extension deconstruction
methods and overloading 363
■
Compiler handling of Deconstruct
calls 364
12.3 Introduction to pattern matching 365
12.4 Patterns available in C# 7.0 367
Constant patterns 367
■ Type patterns
368
■
The var
pattern 371
12.5 Using patterns with the is operator 372
12.6 Using patterns with switch statements 374
Guard clauses 375
■
Pattern variable scope for case
labels 376
■
Evaluation order of pattern-based switch
statements 377
12.7 Thoughts on usage 379
Spotting deconstruction opportunities 379
■
Spotting pattern
matching opportunities 380
13
Improving efficiency with more pass by reference 381
13.1 Recap: What do you know about ref? 382
13.2 Ref locals and ref returns 385
Ref locals 385
■
Ref returns 390
■
The conditional ?: operator
and ref values (C# 7.2) 392
■
Ref readonly (C# 7.2) 393
13.3 in parameters (C# 7.2) 395
CONTENTS xiv
Compatibility considerations 396
■
The surprising mutability of
in parameters: External changes 397
■
Overloading with in
parameters 398
■
Guidance for in parameters 399
13.4 Declaring structs as readonly (C# 7.2) 401
Background: Implicit copying with read-only variables 401
The readonly modifier for structs 403
■
XML serialization is
implicitly read-write 404
13.5 Extension methods with ref or in parameters
(C# 7.2) 405
Using ref/in parameters in extension methods to avoid copying 405
Restrictions on ref and in extension methods 407
13.6 Ref-like structs (C# 7.2) 408
Rules for ref-like structs 409
■ Span<T> and stackalloc
410
IL representation of ref-like structs 414
14
Concise code in C# 7 415
14.1 Local methods 415
Variable access within local methods 417
■
Local method
implementations 420
■
Usage guidelines 425
14.2 Out variables 427
Inline variable declarations for out parameters 427
■
Restrictions
lifted in C# 7.3 for out variables and pattern variables 428
14.3 Improvements to numeric literals 429
Binary integer literals 429
■
Underscore separators 430
14.4 Throw expressions 431
14.5 Default literals (C# 7.1) 432
14.6 Nontrailing named arguments (C# 7.2) 433
14.7 Private protected access (C# 7.2) 435
14.8 Minor improvements in C# 7.3 435
Generic type constraints 435
■
Overload resolution
improvements 436
■
Attributes for fields backing automatically
implemented properties 437
15
C# 8 and beyond 439
15.1 Nullable reference types 440
What problem do nullable reference types solve? 440
■
Changing
the meaning when using reference types 441
■
Enter nullable
reference types 442
■
Nullable reference types at compile time and
CONTENTS xv
execution time 443
■
The damn it or bang operator 445
Experiences of nullable reference type migration 447
Future improvements 449
15.2 Switch expressions 453
15.3 Recursive pattern matching 455
Matching properties in patterns 455
■
Deconstruction
patterns 456
■
Omitting types from patterns 457
15.4 Indexes and ranges 458
Index and Range types and literals 458
■
Applying indexes and
ranges 459
15.5 More async integration 461
Asynchronous resource disposal with using await 461
Asynchronous iteration with foreach await 462
■
Asynchronous
iterators 465
15.6 Features not yet in preview 466
Default interface methods 466
■
Record types 468
Even more features in brief 469
15.7 Getting involved 470
appendix Language features by version 473
index 479
评论